package dti;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import common.HistoWord;
import common.IndexWordUtil;
import common.StopWordFile;

public class WordHistogram {
  private HashMap wordMap;
  private int totalWordCount = 0;
    
  public WordHistogram(char[] breakChars, StopWordFile stopWordFile, File fileToCount) 
  throws FileNotFoundException, IOException {
    wordMap = new HashMap();
    BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(fileToCount));
    IndexWordUtil wordUtil = new IndexWordUtil(breakChars);
    for (String word = wordUtil.getNextWord(inputStream); word != null; word = wordUtil.getNextWord(inputStream)) {
      totalWordCount++;
      word = word.toLowerCase();
      if (!stopWordFile.isStopWord(word)) {
        incrementWordCount(word);
      }
    }
  }
  
  /**
   * Increments the counter for the word passed in
   * @param word
   */
  private void incrementWordCount(String word) {
    if (wordMap.containsKey(word)) {
      Integer count = (Integer) wordMap.get(word);
      int newCount = count.intValue() + 1;
      count = new Integer(newCount);
      wordMap.put(word, count);
    }
    else {
      wordMap.put(word, new Integer(1));
    }
  }
  
  public int getTotalWordCount() {
    return totalWordCount;
  }

  /**
   * Returns a Map:<br>
   * key = word<br>
   * value = count
   */
  public Map getHistogram() {
    return wordMap;
  }  
  
  /**
   * Trims off all words with a count equal to the threshold, then applies the upper
   * and lower bound and returns a trimmed map containing terms up to the max number of bytes specified.<br>
   * Expects a Map:<br>
   * key = word (String)<br>
   * value = count (Integer)
   * 
   * @param upperBound a percentage of the upper bound to trim off
   * @param lowerBound a percentage of the lower bound to trim off
   * 
   * @return the trimmed Map
   */
  public static Map trim(Map mapToTrim, int threshold, double upperBound, double lowerBound) {
    //  add all the words to a list
    ArrayList listOfWords = new ArrayList();
    Iterator itr = mapToTrim.keySet().iterator();
    while (itr.hasNext()) {
      Object word = itr.next();
      Integer count = (Integer) mapToTrim.get(word);
      if(count.intValue() > threshold) 
        listOfWords.add(new HistoWord(word.toString(), count));
      else
        itr.remove();
    }
    
    //order the list then do the trimming
    Collections.sort(listOfWords); //sorted in ascending order  
    int listSize = listOfWords.size();
    
    double trimUpper = Math.floor(upperBound * listSize); //number of words to trim from the upper portion of the list
    if(trimUpper < listSize) {
      int index = listSize;
      for(int i = 0; i < trimUpper; i++) {
        index--;
        HistoWord word = (HistoWord)listOfWords.remove(index);
        mapToTrim.remove(word.text);
      }
    }
    
    double trimLower = Math.floor(lowerBound * listSize); //number of words to trim from the lower portion of the list
    if(trimLower < listSize) {
      for(int i = 0; i < trimLower; i++) {
        HistoWord word = (HistoWord)listOfWords.remove(i);
        mapToTrim.remove(word.text);
      }
    }
    
    return mapToTrim;
  }

}